// Copyright (c) 2021 Onur Cinar. All Rights Reserved.
// The source code is provided under MIT License.
//
// https://github.com/cinar/indicator

package indicator

// Chande forecast oscillator strategy.
func ChandeForecastOscillatorStrategy(asset *Asset) []Action {
	actions := make([]Action, len(asset.Date))

	cfo := ChandeForecastOscillator(asset.Closing)

	for i := 0; i < len(actions); i++ {
		if cfo[i] < 0 {
			actions[i] = BUY
		} else if cfo[i] > 0 {
			actions[i] = SELL
		} else {
			actions[i] = HOLD
		}
	}

	return actions
}

// Moving chande forecast oscillator strategy function.
func MovingChandeForecastOscillatorStrategy(period int, asset *Asset) []Action {
	actions := make([]Action, len(asset.Date))

	cfo := MovingChandeForecastOscillator(period, asset.Closing)

	for i := 0; i < len(actions); i++ {
		if cfo[i] < 0 {
			actions[i] = BUY
		} else if cfo[i] > 0 {
			actions[i] = SELL
		} else {
			actions[i] = HOLD
		}
	}

	return actions
}

// Make moving chande forecast oscillator strategy.
func MakeMovingChandeForecastOscillatorStrategy(period int) StrategyFunction {
	return func(asset *Asset) []Action {
		return MovingChandeForecastOscillatorStrategy(period, asset)
	}
}

// The KdjStrategy function uses the k, d, j values that are generated by
// the Kdj indicator function to provide a BUY action when k crosses
// above d and j. It is stronger when below 20%. Also the SELL
// action is when k crosses below d and j. It is strong when
// above 80%.
//
// Returns actions.
func KdjStrategy(rPeriod, kPeriod, dPeriod int, asset *Asset) []Action {
	actions := make([]Action, len(asset.Date))

	k, d, j := Kdj(rPeriod, kPeriod, dPeriod, asset.High, asset.Low, asset.Closing)

	for i := 0; i < len(actions); i++ {
		if (k[i] > d[i]) && (k[i] > j[i]) && (k[i] <= 20) {
			actions[i] = BUY
		} else if (k[i] < d[i]) && (k[i] < j[i]) && (k[i] >= 80) {
			actions[i] = SELL
		} else {
			actions[i] = HOLD
		}
	}

	return actions
}

// Make KDJ strategy function.
func MakeKdjStrategy(rPeriod, kPeriod, dPeriod int) StrategyFunction {
	return func(asset *Asset) []Action {
		return KdjStrategy(rPeriod, kPeriod, dPeriod, asset)
	}
}

// Default KDJ strategy function.
func DefaultKdjStrategy(asset *Asset) []Action {
	return KdjStrategy(9, 3, 3, asset)
}

// MACD strategy.
func MacdStrategy(asset *Asset) []Action {
	actions := make([]Action, len(asset.Date))

	macd, signal := Macd(asset.Closing)

	for i := 0; i < len(actions); i++ {
		if macd[i] > signal[i] {
			actions[i] = BUY
		} else if macd[i] < signal[i] {
			actions[i] = SELL
		} else {
			actions[i] = HOLD
		}
	}

	return actions
}

// Trend strategy. Buy when trending up for count times,
// sell when trending down for count times.
func TrendStrategy(asset *Asset, count uint) []Action {
	actions := make([]Action, len(asset.Date))

	if len(actions) == 0 {
		return actions
	}

	lastClosing := asset.Closing[0]
	trendCount := uint(1)
	trendUp := false

	actions[0] = HOLD

	for i := 1; i < len(actions); i++ {
		closing := asset.Closing[i]

		if trendUp && (lastClosing <= closing) {
			trendCount++
		} else if !trendUp && (lastClosing >= closing) {
			trendCount++
		} else {
			trendUp = !trendUp
			trendCount = 1
		}

		lastClosing = closing

		if trendCount >= count {
			if trendUp {
				actions[i] = BUY
			} else {
				actions[i] = SELL
			}
		} else {
			actions[i] = HOLD
		}
	}

	return actions
}

// Make trend strategy function.
func MakeTrendStrategy(count uint) StrategyFunction {
	return func(asset *Asset) []Action {
		return TrendStrategy(asset, count)
	}
}

// The VwmaStrategy function uses SMA and VWMA indicators to provide
// a BUY action when VWMA is above SMA, and a SELL signal when VWMA
// is below SMA, a HOLD signal otherwse.
//
// Returns actions
func VwmaStrategy(asset *Asset, period int) []Action {
	actions := make([]Action, len(asset.Date))

	sma := Sma(period, asset.Closing)
	vwma := Vwma(period, asset.Closing, asset.Volume)

	for i := 0; i < len(actions); i++ {
		if vwma[i] > sma[i] {
			actions[i] = BUY
		} else if vwma[i] < sma[i] {
			actions[i] = SELL
		} else {
			actions[i] = HOLD
		}
	}

	return actions
}

// Makes a VWMA strategy for the given period.
func MakeVwmaStrategy(period int) StrategyFunction {
	return func(asset *Asset) []Action {
		return VwmaStrategy(asset, period)
	}
}

// Default VWMA strategy function.
func DefaultVwmaStrategy(asset *Asset) []Action {
	return VwmaStrategy(asset, 20)
}
